import allure
import requests
from requests import JSONDecodeError

from handlers.handler_exception.testcase_execution_faile_exception import TestcaseExecutionFailedException
from handlers.handler_files.handler_ini import HandlerIni
from handlers.handler_files.handler_yaml import HandlerYaml
from handlers.handler_response import HandlerResponse
from handlers.handler_log import logger
from handlers.handler_basics import get_file_binary
from handlers.handler_basics import object_to_str, get_nodes_data, dict_to_format_string, is_none_dict
from handlers.handler_special_expression import HandlerSpecialExpression


class HandlerSendRequest:

    def __init__(self, parametrizes_data):
        try:
            self.cookie = requests.Session()
            self.handler_yaml = HandlerYaml()
            self.handler_ini = HandlerIni()
            self.handler_response = HandlerResponse()
            self.parametrizes_data = parametrizes_data
            self.handler_special_expression = HandlerSpecialExpression()
        except Exception as e:
            logger.error(f'初始化异常：{e}')

    def send_request(self, module_name: str, yaml_name: str):
        # 接口基础信息
        interface_data = self.handler_yaml.reader_interface_data(module_name=module_name, yaml_name=yaml_name)

        # 解析和替换：测试用例的参数化信息
        self.parametrizes_data = self.handler_special_expression.specific_expression_find_parse_replay(
            data=self.parametrizes_data,
            parametrize_data=self.parametrizes_data
        )

        # 解析和替换：接口基础信息
        interface_data = self.handler_special_expression.specific_expression_find_parse_replay(
            data=interface_data,
            parametrize_data=self.parametrizes_data
        )

        # 接口url
        url = get_nodes_data(interface_data, 'requests', 'url')
        # 请求方式
        method = get_nodes_data(interface_data, 'requests', 'method')
        # 请求头
        headers = get_nodes_data(interface_data, 'requests', 'headers')
        # 请求cookies
        cookies_data = interface_data.get('requests').get('cookies', None)
        # 请求files
        file_objects = None
        i = self.handler_yaml.get_title_position(module_name=module_name, yaml_name=yaml_name, title_name='files')
        if i is not None:
            # 从参数化数据中获取断言数据
            files = self.parametrizes_data[i]
            file_objects = dict()
            if is_none_dict(files):
                for files_name, files_path in files.items():
                    # key是name名， value是可以操作文件的对象
                    value = get_file_binary(files_path)
                    file_objects.update({files_name: value})
        # 请求数据获取
        json_data = interface_data.get('requests').get('json', None)
        # 请求数据获取
        data_data = interface_data.get('requests').get('data', None)
        # data类型的数据还需要处理
        if data_data is not None:
            data_data = dict_to_format_string(data_data)
        # 请求数据获取
        params_data = interface_data.get('requests').get('params', None)

        # 响应数据
        response = self.__send_request(
            method=method, url=url, headers=headers, json=json_data, data=data_data, params=params_data,
            cookies=cookies_data, files=file_objects
        )

        # 断言基础信息
        assertion_failed_number = 0
        i = self.handler_yaml.get_title_position(module_name=module_name, yaml_name=yaml_name, title_name='asserts')
        if i is not None and self.parametrizes_data[i] is not None:
            # 从参数化数据中获取断言数据
            assert_data = self.parametrizes_data[i]
            # 断言结果
            assertion_failed_number = self.handler_response.response_all_assert(assert_data, response)
            try:
                assert assertion_failed_number == 0, f".... 断言结果: failed, 断言失败次数: '{assertion_failed_number}'....."
                logger.success('....断言结果: successfully.....')
            except AssertionError as e:
                logger.error(e)

        # 需要提取的数据
        extract_failed_numbers = 0
        i = self.handler_yaml.get_title_position(module_name=module_name, yaml_name=yaml_name, title_name='extracts')
        if i is not None and self.parametrizes_data[i] is not None:
            extract_data = self.parametrizes_data[i]
            # 传入提取位置和响应数据
            extract_failed_numbers = self.handler_response.response_result_extract_dict(response, extract_data)
            if extract_failed_numbers == 0:
                logger.success('...."数据提取到extracts.yaml文件全部成功.....')
            else:
                logger.error(f"....数据提取到extracts.yaml文件失败！ 失败次数为：'{extract_failed_numbers}'.....")

        # 最后判断测试用例是通过还是不通过
        if extract_failed_numbers == 0 and assertion_failed_number == 0:
            logger.success('测试用例执行。。。。。。。。。。。通过。。。。。。。。。。。。。。。。。')
        else:
            logger.success('测试用例执行。。。。。。。。。。。失败。。。。。。。。。。。。。。。。。')
            raise TestcaseExecutionFailedException()

    def __send_request(
            self, method, url, headers, data=None, json=None,
            params=None, files=None, cookies=None, **kwargs
    ) -> requests.Response:
        """
        发送http请求,获取请求数据和响应数据。
        :param cookies: cookies数据1
        :param files: 文件数据
        :param params: 参数数据
        :param json: json数据
        :param data: data数据
        :param headers: 请求头数据
        :param url: 请求路径
        :param method: 请求方法
        :param kwargs: 请求需要的一些列参数，例如：url、method、data、等
        :return: 返回响应结果
        """
        response = None

        try:
            response = self.cookie.request(
                method=method, url=url, headers=headers, data=data, json=json,
                params=params, files=files, cookies=cookies, **kwargs
            )

            allure.attach(
                name="请求路径URL",
                body=url,
                attachment_type=allure.attachment_type.TEXT
            )

            allure.attach(
                name="请求方式",
                body=method,
                attachment_type=allure.attachment_type.TEXT
            )

            allure.attach(
                name="请求头",
                body=object_to_str(response.request.headers),
                attachment_type=allure.attachment_type.JSON
            )

            allure.attach(
                name="响应头",
                body=object_to_str(response.headers),
                attachment_type=allure.attachment_type.JSON)

            allure.attach(
                name="自己发送的cookie信息",
                body=object_to_str(cookies),
                attachment_type=allure.attachment_type.TEXT)

            if json is not None:
                allure.attach(
                    name="请求数据类型json",
                    body=object_to_str(json),
                    attachment_type=allure.attachment_type.JSON)
            if data is not None:
                allure.attach(
                    name="请求数据内容data",
                    body=data,
                    attachment_type=allure.attachment_type.TEXT)
            if params is not None:
                allure.attach(
                    name="请求文件params",
                    body=params,
                    attachment_type=allure.attachment_type.TEXT)

                allure.attach(
                    name="测试结果信息.......................................................................................",
                    body=object_to_str(response.json()),
                    attachment_type=allure.attachment_type.JSON)

        except JSONDecodeError as e:
            logger.error(f"response.text is '{response.text}'  , Causing abnormal JSON format conversion! ")
        except Exception as e:
            logger.exception(f'发送http请求异常：{e}')
        finally:
            return response

